package wire

import (
	"bytes"
	"code.google.com/p/rog-go/values"
	"fmt"
	"reflect"
	"strings"
)

// Func represents a wire function. A regular Go function may be
// use to create a wire function following the rules laid out
// in NewFunc below. A wire function has a type - see FuncType
// for details.
type Func struct {
	f reflect.Value // function to call.
	t *FuncType
}

// FuncType represents the type of a wire function.
// A wire function has some number of initial
// named parameters (each with a single value type),
// any number of subsequent argument types,
// and a return (out) type.
type FuncType struct {
	out    *Type        // ret.actualType==nil if no return type.
	flags  reflect.Type // nil if no options.
	arg    []*Type      // other arguments
	fields map[string]*option
}

type Type struct {
	actualType reflect.Type
	elemType   reflect.Type
}

func (t *Type) String() string {
	s := t.elemType.String()
	if t.actualType != t.elemType {
		s = "«" + s + "»"
	}
	return s
}

func (t *Type) Type() reflect.Type {
	return t.elemType
}

type option struct {
	name     string
	fieldNum int
	typ      *Type
}

// A Param holds a single flag argument to a wire function.
type Param struct {
	Name  string
	Value interface{}
}

type Params []Param

const (
	Const = iota
	Var
)

var osErrorType = reflect.TypeOf((*error)(nil)).Elem()
var valueType = reflect.TypeOf((*values.Value)(nil)).Elem()
var boolType = reflect.TypeOf(false)

var voidType = &Type{reflect.TypeOf(VoidValue), reflect.TypeOf(VoidValue)}

// Type returns the Func's type.
func (f *Func) Type() *FuncType {
	return f.t
}

func (t *FuncType) String() string {
	buf := new(bytes.Buffer)
	prev := false
	for name, opt := range t.fields {
		if prev {
			buf.WriteRune(' ')
		}
		buf.WriteRune('-')
		buf.WriteString(name)
		if opt.typ.actualType != boolType {
			buf.WriteRune('=')
			buf.WriteString(opt.typ.String())
		}
		prev = true
	}
	for i := 0; i < t.NumIn(); i++ {
		if prev {
			buf.WriteRune(' ')
		}
		buf.WriteString(t.In(i).String())
		prev = true
	}
	if prev {
		buf.WriteString("-> ")
	}
	buf.WriteString(t.Out().String())
	return buf.String()
}

// Out returns the type of the wire function's output parameter.
// If the function returns nothing, the result is Void.
func (t *FuncType) Out() *Type {
	return t.out
}

// In returns the type of the wire function's i'th input argument.
func (t *FuncType) In(i int) *Type {
	return t.arg[i]
}

// NumIn returns the number of parameters to a function with type t.
func (t *FuncType) NumIn() int {
	return len(t.arg)
}

//func (t *FuncType) Option

// NewFunc creates a new Func from a function.
// The arguments to the function give the inputs
// to the Func. If the first argument to the function is a struct,
// each field in the struct becomes an option to the Func
// (the field must be exported; the option name is lower cased).
//
// The function may return 0, 1 or 2 values.
// If it returns >0 values, the final
// value must be error. If it returns 2 values,
// the first value returns the result of the operation.
//
// For option, argument and return types, the type
// of the Func type is the same, unless the type is of
// the form:
//   struct {values.Value; _ T}
// which defines a time-varying value. The actual
// type held in the Value is given by T.
func NewFunc(fn interface{}) *Func {
	fv := reflect.ValueOf(fn)
	ft := fv.Type()

	if ft.Kind() != reflect.Func {
		panicf("NewOp: expected function type, got %v", ft)
	}
	t := new(FuncType)
	f := &Func{fv, t}
	nout := ft.NumOut()
	switch {
	case nout > 2:
		panicf("NewFunc: too many return values")
	case nout > 0 && ft.Out(nout-1) != osErrorType:
		panicf("NewFunc: last return value is not error")
	case ft.IsVariadic():
		panicf("NewFunc: variadic functions not supported")
	case nout > 1:
		var err error
		t.out, err = TypeOfType(ft.Out(0))
		if err != nil {
			panicf("NewFunc: return value: %v", err)
		}
	default:
		t.out = voidType
	}

	nin := ft.NumIn()
	a := 0
	// If the first argument is a struct, it gives options.
	if nin > 0 && ft.In(0).Kind() == reflect.Struct && !isValueType(ft.In(0)) {
		t.flags = ft.In(0)
		t.fields = make(map[string]*option)
		if t.flags.NumField() > 63 {
			panicf("too many options in %v", f.t.flags)
		}
		for i := 0; i < t.flags.NumField(); i++ {
			s := t.flags.Field(i)
			wt, err := TypeOfType(s.Type)
			if err != nil {
				panicf("field %s: %v", s.Name, err)
			}
			c := &option{
				name:     strings.ToLower(s.Name),
				fieldNum: i,
				typ:      wt,
			}
			t.fields[c.name] = c
		}
		a++
	}
	t.arg = make([]*Type, nin-a)
	for i := a; i < nin; i++ {
		at, err := TypeOfType(ft.In(i))
		if err != nil {
			panicf("arg %d: %v", i, err)
		}
		t.arg[i-a] = at
	}
	return f
}

func isValueType(t reflect.Type) bool {
	wt, err := TypeOfType(t)
	return err == nil && wt.elemType != wt.actualType
}

// Call calls the Func. Any arguments that are not already of the required type
// will be converted via convert if possible before the call. The return value
// should be pointed to by ret, and will be similarly converted if necessary
// after the call before being stored. If ret is nil, it is assumed to point to
// Void.
//
// Each named parameter will be stored
// in the struct which is the first argument to the underlying function.
// Call will return an error if there are named parameters that do not exist
// in the function or if any argument value cannot be converted to the
// required type.
//
func (f *Func) Call(convert Conversions, ret interface{}, params []Param, args ...interface{}) (err error) {
	var argCvt []func() error
	var argv []reflect.Value
	firstArg := 0
	if f.t.flags != nil || len(params) > 0 {
		argv = make([]reflect.Value, len(f.t.arg)+1)
		argv[0], err = f.makeOpts(convert, &argCvt, params)
		if err != nil {
			return err
		}
		firstArg = 1
	} else {
		argv = make([]reflect.Value, len(f.t.arg))
	}
	err = f.makeArgs(convert, &argCvt, argv[firstArg:], args)
	if err != nil {
		return err
	}
	if ret == nil {
		ret = &VoidValue
	}
	retCvt, err := f.makeRet(convert, ret)
	if err != nil {
		return err
	}
	// perform all argument conversions
	for _, cvt := range argCvt {
		if err := cvt(); err != nil {
			return err
		}
	}
	rv := f.f.Call(argv)
	if len(rv) == 0 {
		return nil
	}
	err = asOsError(rv[len(rv)-1])
	if err != nil {
		return err
	}
	if len(rv) > 1 {
		err = retCvt(rv[0])
	}
	return
}

var nilv reflect.Value

func (f *Func) makeOpts(convert Conversions, argCvt *[]func() error, params []Param) (optv reflect.Value, err error) {
	if f.t.flags == nil {
		if len(params) > 0 {
			err = fmt.Errorf("invalid parameter name %q", params[0].Name)
		}
		return
	}
	optv = reflect.New(f.t.flags).Elem()
	set := int64(0)
	for _, p := range params {
		c, ok := f.t.fields[p.Name]
		if !ok {
			return nilv, fmt.Errorf("invalid parameter name %q", p.Name)
		}
		m := int64(1) << uint(c.fieldNum)
		if set&m != 0 {
			return nilv, fmt.Errorf("duplicate parameter %q", p.Name)
		}
		set |= m
		fv := optv.Field(c.fieldNum)
		pv := reflect.ValueOf(p.Value)
		pt, err := TypeOfType(pv.Type())
		if err != nil {
			return nilv, fmt.Errorf("parameter %q: %v", p.Name, err)
		}
		if pt.eq(c.typ) {
			fv.Set(pv)
			continue
		}
		// types are not equal, see if we can convert
		cvt, err := genConvertFunc(convert, pt, c.typ)
		if err != nil {
			return nilv, fmt.Errorf("param %q: cannot convert from %s to %s: %v", p.Name, pt, c.typ, err)
		}
		*argCvt = append(*argCvt, func() error {
			return cvt(fv, pv)
		})
	}
	return optv, nil
}

func (f *Func) makeArgs(convert Conversions, argCvt *[]func() error, argv []reflect.Value, args []interface{}) error {
	if len(args) != len(f.t.arg) {
		return fmt.Errorf("invalid parameter count; expected %d got %d", len(f.t.arg), len(args))
	}
	for i, argt := range f.t.arg {
		pv := reflect.ValueOf(args[i])
		pt, err := TypeOfType(pv.Type())
		if err != nil {
			return fmt.Errorf("arg %d: %v", err)
		}
		if pt.eq(argt) {
			argv[i] = pv
			continue
		}
		// types are not equal, see if we can convert
		cvt, err := genConvertFunc(convert, pt, argt)
		if err != nil {
			return fmt.Errorf("arg %d: (from %v to %v), %v", i, pt, argt, err)
		}
		av := reflect.New(argt.actualType).Elem()
		argv[i] = av
		*argCvt = append(*argCvt, func() error { return cvt(pv, av) })
	}
	return nil
}

// makeRet returns a function that can store the return value of the Func
// into ret. If it cannot, for instance because there is no available conversion,
// it returns an error.
func (f *Func) makeRet(convert Conversions, ret interface{}) (retCvt func(srcv reflect.Value) error, err error) {
	retv := reflect.ValueOf(ret)
	if retv.Kind() != reflect.Ptr {
		err = fmt.Errorf("return parameter expected pointer, got %v", retv.Type())
		return
	}
	retv = retv.Elem()
	rett, err := TypeOfType(retv.Type())
	if err != nil {
		err = fmt.Errorf("bad return type: %v", err)
		return
	}
	if rett.eq(f.t.out) {
		retCvt = func(srcv reflect.Value) error {
			retv.Set(srcv)
			return nil
		}
		return
	}
	cvt, err := genConvertFunc(convert, f.t.out, rett)
	if cvt == nil {
		err = fmt.Errorf("cannot convert return type: %v", err)
		return
	}
	retCvt = func(srcv reflect.Value) error {
		return cvt(srcv, retv)
	}
	return
}

// genConvertFunc returns a converter that can convert from
// srct to dstt, including between values.Value types.
// If there is no conversion available, it returns an error.
func genConvertFunc(cvt Conversions, srct, dstt *Type) (Converter, error) {
	if dstt.eq(srct) {
		return identity, nil
	}
	src2dst := identity
	dst2src := identity
	if srct.elemType != dstt.elemType {
		src2dst = cvt.Converter(srct.elemType, dstt.elemType)
		if src2dst == nil {
			return nil, fmt.Errorf("no conversion found from %v to %v", srct.elemType, dstt.elemType)
		}
		dst2src = cvt.Converter(dstt.elemType, srct.elemType)
	}
	switch {
	case dstt.isVar() && srct.isVar():
		// Var to Var (with conversion, otherwise dstt would equal srct)
		if dst2src == nil {
			return nil, fmt.Errorf("no bidirectional conversion available between %v and %v", srct.elemType, dstt.elemType)
		}
		lens := values.NewReflectiveLens(
			func(v reflect.Value) (reflect.Value, error) {
				dstv := reflect.New(dstt.elemType).Elem()
				err := src2dst(v, dstv)
				return dstv, err
			},
			func(v reflect.Value) (reflect.Value, error) {
				srcv := reflect.New(srct.elemType).Elem()
				err := dst2src(v, srcv)
				return srcv, err
			},
			srct.elemType,
			dstt.elemType,
		)
		return func(srcv, dstv reflect.Value) error {
			//fmt.Printf("transforming %v(%v)->%v(%v)\n", srcv.Type(), srcv.Kind(), dstv.Type(), dstv.Kind())
			srcval := srcv.Field(0).Interface().(values.Value)
			transformedVal := values.Transform(srcval, lens)
			dstval := reflect.New(dstt.actualType).Elem()
			dstval.Field(0).Set(reflect.ValueOf(transformedVal))
			dstv.Set(dstval)
			return nil
		}, nil
	case srct.isVar():
		// Var to Const
		return nil, fmt.Errorf("cannot convert from variable (%v) to const (%v)", srct, dstt)
	case dstt.isVar() && srct.elemType == dstt.elemType:
		// Const to Var with no conversion
		return func(srcv, dstv reflect.Value) error {
			val := values.NewConst(srcv.Interface(), dstt.elemType)
			dstv.Field(0).Set(reflect.ValueOf(val))
			return nil
		}, nil
	case dstt.isVar():
		// Const to Var with conversion
		return func(srcv, dstv reflect.Value) error {
			v := reflect.New(dstt.elemType).Elem()
			if err := src2dst(srcv, v); err != nil {
				return err
			}
			val := values.NewConst(v.Interface(), dstt.elemType)
			dstv.Field(0).Set(reflect.ValueOf(val))
			return nil
		}, nil
	}
	// Const to Const
	return func(srcv, dstv reflect.Value) error {
		return src2dst(srcv, dstv)
	}, nil
}

// asOsError returns the given value, which is known
// to be of type error, as an error interface.
// This is because v.Interface().(error) will fail
// if the value is nil.
func asOsError(v reflect.Value) error {
	val, _ := v.Interface().(error)
	return val
}

// isVar returns true if the t represents a time-varying type.
func (t *Type) isVar() bool {
	return t.actualType != t.elemType
}

func (t *Type) eq(t1 *Type) bool {
	return t.actualType == t1.actualType &&
		t.elemType == t1.elemType
}

// TypeOfType returns the "wire" type of x - if x is of the
// form struct {values.Value; _ T}, then the type
// is taken to represent a values.Value with element type T.
func TypeOfType(x reflect.Type) (*Type, error) {
	if x.Kind() == reflect.Interface {
		return nil, fmt.Errorf("wire type %v cannot be interface", x)
	}
	if x.Kind() != reflect.Struct {
		return &Type{x, x}, nil
	}
	if x.NumField() != 2 ||
		!x.Field(0).Anonymous ||
		x.Field(0).Type != valueType ||
		x.Field(1).Name != "_" {
		return &Type{x, x}, nil
	}
	return &Type{x, x.Field(1).Type}, nil
}

func panicf(f string, a ...interface{}) {
	panic(fmt.Errorf(f, a...))
}
